﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.IO;
using System.Data.Common;

public partial class Backup : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        lblNoBackupOptions.Visible = false;
        lblNoFileSelected.Visible = false;
        lblErrorRestore.Visible = false;
        restoreErrors.Visible = false;
        lstRestoreErrors.Items.Clear();
    }
    protected void btnBackup_Click(object sender, EventArgs e) {
        if(!chkBakApplications.Checked && !chkBakElements.Checked && !chkBakTranslations.Checked && !chkBakUsers.Checked) {
            lblNoBackupOptions.Visible = true;
            return;
        }
        XmlDataDocument doc = new XmlDataDocument();
        XmlDeclaration decl = (XmlDeclaration)doc.CreateNode(XmlNodeType.XmlDeclaration, "", "");
        decl.Encoding = "UTF-8";
        doc.AppendChild(decl);
        XmlNode root = doc.CreateElement("resourceblender");
        doc.AppendChild(root);

        XmlElement ndMeta = doc.CreateElement("meta");
        root.AppendChild(ndMeta);
        XmlElement ndGenerator = doc.CreateElement("generator");
        ndGenerator.InnerText = "ResourceBlender "+Constants.Version.ToString(3);
        ndMeta.AppendChild(ndGenerator);
        XmlElement ndGenerationDate = doc.CreateElement("generated");
        ndGenerationDate.InnerText = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-dd HH:mm:ss G\\MT");
        ndMeta.AppendChild(ndGenerationDate);

        Database db = new Database();
        db.OpenConnection();
        if(chkBakApplications.Checked) {
            using(Database.Command cmd = db.CreateCommand()) {
                XmlElement ndApplications = doc.CreateElement("applications");
                root.AppendChild(ndApplications);
                cmd.CommandText = "SELECT * FROM applications";
                DbDataReader reader = cmd.ExecuteReader();
                while(reader.Read()) {
                    XmlElement eleApplication = doc.CreateElement("application");
                    ndApplications.AppendChild(eleApplication);

                    XmlElement eleId = doc.CreateElement("id");
                    eleId.InnerText = HttpUtility.HtmlEncode(reader["id"].ToString());
                    eleApplication.AppendChild(eleId);

                    XmlElement eleName = doc.CreateElement("name");
                    eleName.InnerText = Utils.HtmlEncode(reader["name"].ToString());
                    eleApplication.AppendChild(eleName);

                    XmlElement eleDisplayname = doc.CreateElement("displayname");
                    eleDisplayname.InnerText = Utils.HtmlEncode(reader["displayname"].ToString());
                    eleApplication.AppendChild(eleDisplayname);

                    XmlElement eleType = doc.CreateElement("type");
                    eleType.InnerText = HttpUtility.HtmlEncode(reader["type"].ToString());
                    eleApplication.AppendChild(eleType);

                    XmlElement eleDefaultLanguage = doc.CreateElement("defaultlanguage");
                    eleDefaultLanguage.InnerText = HttpUtility.HtmlEncode(reader["defaultlanguage"].ToString());
                    eleApplication.AppendChild(eleDefaultLanguage);

                    XmlElement eleLanguages = doc.CreateElement("languages");
                    eleLanguages.InnerText = HttpUtility.HtmlEncode(reader["languages"].ToString());
                    eleApplication.AppendChild(eleLanguages);

                    XmlElement eleBundles = doc.CreateElement("bundles");
                    eleBundles.InnerText = Utils.HtmlEncode(reader["bundles"].ToString());
                    eleApplication.AppendChild(eleBundles);
                }
                reader.Close();
            }
        }
        if(chkBakElements.Checked) {
            using(Database.Command cmd = db.CreateCommand()) {
                XmlElement ndElements = doc.CreateElement("elements");
                root.AppendChild(ndElements);
                cmd.CommandText = "SELECT * FROM elements";
                DbDataReader reader = cmd.ExecuteReader();
                while(reader.Read()) {
                    XmlElement eleElement = doc.CreateElement("element");
                    ndElements.AppendChild(eleElement);

                    XmlElement eleApplicationId = doc.CreateElement("applicationid");
                    eleApplicationId.InnerText = HttpUtility.HtmlEncode(reader["applicationid"].ToString());
                    eleElement.AppendChild(eleApplicationId);

                    XmlElement eleBundleName = doc.CreateElement("bundlename");
                    eleBundleName.InnerText = Utils.HtmlEncode(reader["bundlename"].ToString());
                    eleElement.AppendChild(eleBundleName);

                    XmlElement eleElementName = doc.CreateElement("elementname");
                    eleElementName.InnerText = Utils.HtmlEncode(reader["elementname"].ToString());
                    eleElement.AppendChild(eleElementName);

                    XmlElement eleTranslationId = doc.CreateElement("translationid");
                    eleTranslationId.InnerText = reader["translationid"].ToString();
                    eleElement.AppendChild(eleTranslationId);

                    XmlElement eleComment = doc.CreateElement("comment");
                    eleComment.InnerText = Utils.HtmlEncode(reader["comment"].ToString());
                    eleElement.AppendChild(eleComment);
                }
                reader.Close();
            }
        }
        if(chkBakTranslations.Checked) {
            using(Database.Command cmd = db.CreateCommand()) {
                XmlElement ndTranslations = doc.CreateElement("translations");
                root.AppendChild(ndTranslations);
                cmd.CommandText = "SELECT translations.translationid AS translationid, language, translation, comment FROM translations LEFT JOIN translationcomments ON translations.translationid = translationcomments.translationid ORDER BY translations.translationid ASC";
                DbDataReader reader = cmd.ExecuteReader();
                string lastTranslationId = string.Empty,
                       currentTranslationId = string.Empty;
                XmlElement eleTranslation = null;
                while(reader.Read()) {
                    currentTranslationId = reader["translationid"].ToString();
                    if(currentTranslationId != lastTranslationId) {
                        eleTranslation = doc.CreateElement("translation");
                        ndTranslations.AppendChild(eleTranslation);

                        XmlElement eleId = doc.CreateElement("id");
                        eleId.InnerText = HttpUtility.HtmlEncode(currentTranslationId);
                        eleTranslation.AppendChild(eleId);

                        XmlElement eleComment = doc.CreateElement("comment");
                        eleComment.InnerText = Utils.HtmlEncode(reader["comment"].ToString());
                        eleTranslation.AppendChild(eleComment);

                        XmlElement eleLanguages = doc.CreateElement("languages");
                        eleTranslation.AppendChild(eleLanguages);
                    }
                    lastTranslationId = currentTranslationId;
                    XmlElement eleTranslatedLanguage = doc.CreateElement("language");
                    eleTranslatedLanguage.InnerText = Utils.HtmlEncode(reader["translation"].ToString());
                    eleTranslation.LastChild.AppendChild(eleTranslatedLanguage);

                    XmlAttribute attrAbbreviation = doc.CreateAttribute("code");
                    attrAbbreviation.InnerText = reader["language"].ToString();
                    eleTranslatedLanguage.Attributes.Append(attrAbbreviation);
                }
                reader.Close();
            }
        }
        if(chkBakUsers.Checked) {
            using(Database.Command cmd = db.CreateCommand()) {
                XmlElement ndUsers = doc.CreateElement("users");
                root.AppendChild(ndUsers);
                cmd.CommandText = "SELECT * FROM users";
                DbDataReader reader = cmd.ExecuteReader();
                while(reader.Read()) {
                    XmlElement eleUser = doc.CreateElement("user");
                    ndUsers.AppendChild(eleUser);

                    XmlElement eleId = doc.CreateElement("id");
                    eleId.InnerText = HttpUtility.HtmlEncode(reader["id"].ToString());
                    eleUser.AppendChild(eleId);

                    XmlElement eleUsername = doc.CreateElement("username");
                    eleUsername.InnerText = Utils.HtmlEncode(reader["username"].ToString());
                    eleUser.AppendChild(eleUsername);

                    XmlElement elePassword = doc.CreateElement("password");
                    elePassword.InnerText = Utils.HtmlEncode(reader["password"].ToString());
                    eleUser.AppendChild(elePassword);

                    XmlElement eleEmail = doc.CreateElement("email");
                    eleEmail.InnerText = Utils.HtmlEncode(reader["email"].ToString());
                    eleUser.AppendChild(eleEmail);

                    XmlElement elePermissions = doc.CreateElement("permissions");
                    elePermissions.InnerText = HttpUtility.HtmlEncode(reader["permissions"].ToString());
                    eleUser.AppendChild(elePermissions);

                    XmlElement eleAllowedProjects = doc.CreateElement("allowedprojects");
                    eleAllowedProjects.InnerText = Utils.HtmlEncode(reader["allowedprojects"].ToString());
                    eleUser.AppendChild(eleAllowedProjects);
                }
                reader.Close();
            }
        }

        using(MemoryStream ms = new MemoryStream()) {
            doc.Save(ms);
            Response.Clear();
            Response.AddHeader("Content-Type", "binary/octet-stream");
            Response.AddHeader("Content-Length", ms.Length.ToString());
            Response.AddHeader("Content-Disposition", "attachment; filename=ResourceBlender" + DateTime.Now.ToString("yyyy-MM-dd-HH:mm")+".xml; size=" + ms.Length.ToString());
            Response.Flush();

            Response.BinaryWrite(ms.ToArray());
            Response.Flush();
            Response.End();
        }
    }

    /// <summary>
    /// Restores a backup file.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void btnRestore_Click(object sender, EventArgs e) {
        if(!uplBackupfile.HasFile) {
            lblNoFileSelected.Visible = true;
            return;
        } else if(!chkResApplications.Checked && !chkResElements.Checked && !chkResTranslations.Checked && !chkResUsers.Checked) {
            lblErrorRestore.Text = "Nothing selected to restore";
            lblErrorRestore.Visible = true;
            return;
        }
        try {
            Database db = new Database();
            db.OpenConnection();
            XmlDataDocument doc = new XmlDataDocument();
            doc.Load(uplBackupfile.FileContent);
            List<string> errors = new List<string>();
            if(chkClearDatabase.Checked) {
                try {
                    db.DropTables(chkResApplications.Checked, chkResElements.Checked, chkResTranslations.Checked, chkResTranslations.Checked, chkResUsers.Checked);
                } catch { }
                db.CreateTables(chkResApplications.Checked, chkResElements.Checked, chkResTranslations.Checked, chkResTranslations.Checked, chkResUsers.Checked);
            }
            if(chkResApplications.Checked) {
                using(Database.Command cmd = db.CreateCommand()) {
                    cmd.CommandText = "INSERT INTO applications (id, name, displayname, type, defaultlanguage, languages, bundles) VALUES (@id, @name, @displayname, @type, @defaultlanguage, @languages, @bundles)";
                    cmd.AddParameterWithValue("@id", string.Empty);
                    cmd.AddParameterWithValue("@name", string.Empty);
                    cmd.AddParameterWithValue("@displayname", string.Empty);
                    cmd.AddParameterWithValue("@type", string.Empty);
                    cmd.AddParameterWithValue("@defaultlanguage", string.Empty);
                    cmd.AddParameterWithValue("@languages", string.Empty);
                    cmd.AddParameterWithValue("@bundles", string.Empty);
                    XmlNodeList appsNodes = doc.GetElementsByTagName("application");
                    foreach(XmlNode app in appsNodes) {
                        string id = string.Empty,
                                   name = string.Empty,
                                   displayname = string.Empty,
                                   type = string.Empty,
                                   defaultlanguage = string.Empty,
                                   languages = string.Empty,
                                   bundles = string.Empty;
                        foreach(XmlNode property in app) {
                            switch(property.Name.ToLower()) {
                                case "id":
                                    id = HttpUtility.HtmlDecode(property.InnerText);
                                    break;
                                case "name":
                                    name = Utils.HtmlDecode(property.InnerText);
                                    break;
                                case "displayname":
                                    displayname = Utils.HtmlDecode(property.InnerText);
                                    break;
                                case "type":
                                    type = HttpUtility.HtmlDecode(property.InnerText);
                                    break;
                                case "defaultlanguage":
                                    defaultlanguage = HttpUtility.HtmlDecode(property.InnerText);
                                    break;
                                case "languages":
                                    languages = HttpUtility.HtmlDecode(property.InnerText.Trim());
                                    break;
                                case "bundles":
                                    bundles = Utils.HtmlDecode(property.InnerText.Trim());
                                    break;
                            }
                        }
                        if(string.IsNullOrEmpty(id) ||
                           string.IsNullOrEmpty(name) ||
                           string.IsNullOrEmpty(displayname) ||
                           string.IsNullOrEmpty(type) ||
                           string.IsNullOrEmpty(defaultlanguage) ||
                           string.IsNullOrEmpty(languages)) {
                                errors.Add("Invalid application " + displayname);
                        } else {
                            cmd.Parameters["@id"].Value = id;
                            cmd.Parameters["@name"].Value = name;
                            cmd.Parameters["@displayname"].Value = displayname;
                            cmd.Parameters["@type"].Value = type;
                            cmd.Parameters["@defaultlanguage"].Value = defaultlanguage;
                            cmd.Parameters["@languages"].Value = languages;
                            cmd.Parameters["@bundles"].Value = bundles;
                            try {
                                cmd.ExecuteNonQuery();
                            } catch(Exception ex) {
                                errors.Add(ex.Message);
                            }
                        }
                    }
                }
            }
            if(chkResElements.Checked) {
                using(Database.Command cmd = db.CreateCommand()) {
                    cmd.CommandText = "INSERT INTO elements (applicationid, bundlename, elementname, translationid, comment) VALUES (@applicationid, @bundlename, @elementname, @translationid, @comment)";
                    cmd.AddParameterWithValue("@applicationid", string.Empty);
                    cmd.AddParameterWithValue("@bundlename", string.Empty);
                    cmd.AddParameterWithValue("@elementname", string.Empty);
                    cmd.AddParameterWithValue("@translationid", string.Empty);
                    cmd.AddParameterWithValue("@comment", string.Empty);
                    XmlNodeList elementsNodes = doc.GetElementsByTagName("element");
                    foreach(XmlNode element in elementsNodes) {
                        string applicationid = string.Empty,
                                   bundlename = string.Empty,
                                   elementname = string.Empty,
                                   translationid = string.Empty,
                                   comment = string.Empty;
                        foreach(XmlNode property in element) {
                            switch(property.Name.ToLower()) {
                                case "applicationid":
                                    applicationid = HttpUtility.HtmlDecode(property.InnerText);
                                    break;
                                case "bundlename":
                                    bundlename = Utils.HtmlDecode(property.InnerText);
                                    break;
                                case "elementname":
                                    elementname = Utils.HtmlDecode(property.InnerText);
                                    break;
                                case "translationid":
                                    translationid = HttpUtility.HtmlDecode(property.InnerText);
                                    break;
                                case "comment":
                                    comment = Utils.HtmlDecode(property.InnerText);
                                    break;
                            }
                        }
                        if(string.IsNullOrEmpty(applicationid) ||
                           string.IsNullOrEmpty(elementname)) {
                                errors.Add("Invalid element " + elementname);
                        } else {
                            cmd.Parameters["@applicationid"].Value = applicationid;
                            cmd.Parameters["@bundlename"].Value = bundlename;
                            cmd.Parameters["@elementname"].Value = elementname;
                            cmd.Parameters["@translationid"].Value = translationid;
                            cmd.Parameters["@comment"].Value = comment;
                            try {
                                cmd.ExecuteNonQuery();
                            } catch(Exception ex) {
                                errors.Add(ex.Message);
                            }
                        }
                    }
                }
            }
            if(chkResTranslations.Checked) {
                using(Database.Command cmdInsertTranslation = db.CreateCommand())
                using(Database.Command cmdInsertComment = db.CreateCommand()) {
                    cmdInsertTranslation.CommandText = "INSERT INTO translations (translationid, language, translation) VALUES (@id, @language, @translation)";
                    cmdInsertTranslation.AddParameterWithValue("@id", string.Empty);
                    cmdInsertTranslation.AddParameterWithValue("@language", string.Empty);
                    cmdInsertTranslation.AddParameterWithValue("@translation", string.Empty);
                    cmdInsertComment.CommandText = "INSERT INTO translationcommemts (translationid, comment) VALUES (@id, @comment)";
                    cmdInsertComment.AddParameterWithValue("@id", string.Empty);
                    cmdInsertComment.AddParameterWithValue("@comment", string.Empty);
                    XmlNodeList elementsNodes = doc.GetElementsByTagName("translation");
                    foreach(XmlNode element in elementsNodes) {
                        try {
                            string id = element["id"].InnerText,
                                   comment = Utils.HtmlDecode(element["comment"].InnerText),
                                   language = string.Empty,
                                   translation = string.Empty;
                            XmlNodeList ndLanguages = element.SelectNodes("languages/language");
                            if(string.IsNullOrEmpty(id) || ndLanguages.Count == 0) {
                                errors.Add("Invalid translation");
                                continue;
                            }
                            if(!string.IsNullOrEmpty(comment)) {
                                cmdInsertComment.Parameters["@id"].Value = id;
                                cmdInsertComment.Parameters["@comment"].Value = comment;
                                cmdInsertComment.ExecuteNonQuery();
                            }
                            cmdInsertTranslation.Parameters["@id"].Value = id;
                            foreach(XmlNode ndLanguage in ndLanguages) {
                                if(ndLanguage.Attributes.Count > 0) {
                                    cmdInsertTranslation.Parameters["@language"].Value = ndLanguage.Attributes[0].Value;
                                    cmdInsertTranslation.Parameters["@translation"].Value = Utils.HtmlDecode(ndLanguage.InnerText);
                                    try {
                                        cmdInsertTranslation.ExecuteNonQuery();
                                    } catch(Exception ex) {
                                        errors.Add("Could not add translation for translation id " + id+": "+ex.Message);
                                    }
                                }
                            }
                        } catch(Exception ex) {
                            errors.Add("Invalid translation" + ex.Message);
                        }
                    }
                }
            }
            if(chkResUsers.Checked) {
                using(Database.Command cmd = db.CreateCommand()) {
                    cmd.CommandText = "INSERT INTO users (username, password, email, permissions, allowedprojects) VALUES (@username, @password, @email, @permissions, @allowedprojects)";
                    cmd.AddParameterWithValue("@username", string.Empty);
                    cmd.AddParameterWithValue("@password", Utils.SaltPassword("password"));
                    cmd.AddParameterWithValue("@email", string.Empty);
                    cmd.AddParameterWithValue("@permissions", string.Empty);
                    cmd.AddParameterWithValue("@allowedprojects", string.Empty);
                    XmlNodeList appsNodes = doc.GetElementsByTagName("user");
                    foreach(XmlNode app in appsNodes) {
                        string username = string.Empty,
                               email = string.Empty,
                               permissions = string.Empty,
                               allowedprojects = string.Empty;
                        foreach(XmlNode property in app) {
                            switch(property.Name.ToLower()) {
                                case "username":
                                    username = Utils.HtmlDecode(property.InnerText);
                                    break;
                                case "password":
                                    break;
                                case "email":
                                    email = Utils.HtmlDecode(property.InnerText);
                                    break;
                                case "permissions":
                                    permissions = HttpUtility.HtmlDecode(property.InnerText);
                                    break;
                                case "allowedprojects":
                                    allowedprojects = Utils.HtmlDecode(property.InnerText);
                                    break;
                            }
                        }
                        if(string.IsNullOrEmpty(username) ||
                           string.IsNullOrEmpty(permissions)) {
                                errors.Add("Invalid user " + username);
                        } else {
                            cmd.Parameters["@username"].Value = username;
                            cmd.Parameters["@email"].Value = email;
                            cmd.Parameters["@permissions"].Value = permissions;
                            cmd.Parameters["@allowedprojects"].Value = allowedprojects;
                            try {
                                cmd.ExecuteNonQuery();
                            } catch(Exception ex) {
                                errors.Add(ex.Message);
                            }
                        }
                    }
                }
            }
            if(errors.Count > 0) {
                restoreErrors.Visible = true;
                foreach(string error in errors) {
                    lstRestoreErrors.Items.Add(error);
                }
            }
        } catch(Exception ex) {
            restoreErrors.Visible = true;
            lstRestoreErrors.Items.Add("Error restoring #406: " + ex.Message);
        }
    }
}
